#include "kpextVkContext.h"

kpextVkContext * GvkContextPtr = nullptr;

#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS
#ifdef DEBUG
bool kpextVkContext::EnableDebugLayers = true;
#else
bool kpextVkContext::EnableDebugLayers = false;
#endif
#endif

bool kpextVkContext::EnableDebugLayersByProgram = false;

std::shared_ptr<vk::Instance> kpextVkContext::m_instance = nullptr;
vk::DebugUtilsMessengerEXT kpextVkContext::m_debugMessenger{nullptr};
vk::DispatchLoaderDynamic kpextVkContext::m_dynamicDispatcher;

using JSON = nlohmann::json;

bool kpextVkContext::NO_USE_JSON_CONFIG = false;
int kpextVkContext::VK_PHY_DEV_IDX_TO_CREATE = -1;
std::string kpextVkContext::VK_PHY_DEV_PUUID_TO_CREATE{};
std::vector<unsigned int> kpextVkContext::VK_QUEUE_FAMILY_INDICES{};
std::vector<std::string> kpextVkContext::VK_DESIRED_DEV_EXTENSIONS{};
std::vector<unsigned int> kpextVkContext::VK_DEF_COMPUTESHADER_WORKGROUP_SIZE{};
std::vector<unsigned int> kpextVkContext::StagesThreadsCount{};

std::array<uint32_t, 3> calc2DWorkGroup(uint32_t cols, uint32_t rows,
                                        const std::array<uint32_t, 3> workgroupsize)
{
    std::array<uint32_t, 3> ret;
    ret[2] = 1;
    ret[0] = cols + workgroupsize[0] - 1;
    ret[1] = rows + workgroupsize[1] - 1;
    ret[0] = ret[0] / workgroupsize[0];
    ret[1] = ret[1] / workgroupsize[1];
    return ret;
}


/**
     * @brief VK_EXT_debug_utils调试信息扩展功能的回调函数,原型为 PFN_vkDebugUtilsMessengerCallbackEXT
     * @param messageSeverity
     * @param messageType
     * @param pCallbackData
     * @param pUserData
     * @return
     */
static VKAPI_ATTR VkBool32 VKAPI_CALL debugMsgCallback(
        VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,  // 消息类型
        VkDebugUtilsMessageTypeFlagsEXT messageType,
        const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
        void* pUserData) {

    //messageSeverity  参考 VK_DEBUG_UTILS_MESSAGE_SEVERITY_xxxx
    //messageType  参考 VK_DEBUG_UTILS_MESSAGE_TYPE_xxxx
    //pCallbackData 返回VkDebugUtilsMessengerCallbackDataEXT结构数据，包含以下成员
    //  pMessage: 一个以null结尾的包含调试信息的字符串
    //  pObjects: 存储有和消息相关的vulkan对象句柄的数组
    //  objectCount: pObjects的数组长度
    //pUserData是一个指向了我们设置回调函数时，传递的数据的指针。

    UNUSE_VAR(pUserData);
    std::string msgtypes("Unknow");
    if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT){
        msgtypes = "General";
    }
    if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT){
        msgtypes = "Performance";
    }
    if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT){
        msgtypes = "Validation";
    }
    const char msgpattern[] = "vulkan debugMsg MsgType:{}, Msg : \n{}";
    switch (messageSeverity) {
    case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
        SPDLOG_DEBUG(msgpattern,
                     msgtypes, pCallbackData->pMessage);
        break;
    case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
        SPDLOG_INFO(msgpattern,
                    msgtypes, pCallbackData->pMessage);
        break;
    case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
        SPDLOG_WARN(msgpattern, msgtypes, pCallbackData->pMessage);
        break;
    case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
        SPDLOG_ERROR(msgpattern, msgtypes, pCallbackData->pMessage);
        break;
    default:
        SPDLOG_WARN(msgpattern, msgtypes, pCallbackData->pMessage);
        break;
    }

    //    std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;

    //回调函数返回了一个布尔值，用来表示引发校验层处理的调用是否被中断
    //如果返回值为true，对应调用就会返回VK_ERROR_VALIDATION_FAILED_EXT并终止
    //通常，只在测试校验层本身时会返回True，其余情况下，回调函数应该返回False。
    return VK_FALSE;
}


/**
 * @brief 打印vulkan物理设备一些限制信息
 * @param limits
 */
void printPhyDevLimits(const vk::PhysicalDeviceLimits & limits){
    SPDLOG_INFO("maxComputeWorkGroupInvocations: {} (for computeshader)",
                limits.maxComputeWorkGroupInvocations);
    SPDLOG_INFO("maxComputeWorkGroupSize: {} (for computeshader)",
                fmt::format("{}", fmt::join(limits.maxComputeWorkGroupSize,", "))
                );
    SPDLOG_INFO("maxComputeWorkGroupCount: {} (for cmdDispatch)",
                fmt::format("{}", fmt::join(limits.maxComputeWorkGroupCount,", "))
                );
    SPDLOG_INFO("maxPushConstantsSize: {} bytes",
                limits.maxPushConstantsSize);

    SPDLOG_INFO("maxUniformBufferRange: {}",
                limits.maxUniformBufferRange);

    SPDLOG_INFO("maxTexelBufferElements: {}",
                limits.maxTexelBufferElements);

    SPDLOG_INFO("maxPerStageDescriptorUniformBuffers: {}",
                limits.maxPerStageDescriptorUniformBuffers);

    SPDLOG_INFO("maxPerStageDescriptorStorageBuffers: {}",
                limits.maxPerStageDescriptorStorageBuffers);

    SPDLOG_INFO("maxPerStageDescriptorStorageImages: {}",
                limits.maxPerStageDescriptorStorageImages);
}

kpextVkContext::kpextVkContext()
{
    kpextVkContext::readJsonCfg();
    kpextVkContext::createInstance();
    kpextVkContext::createDevice(VK_QUEUE_FAMILY_INDICES,
                                 static_cast<unsigned int>(VK_PHY_DEV_IDX_TO_CREATE),
                                 VK_DESIRED_DEV_EXTENSIONS
                                 );
}

kpextVkContext::~kpextVkContext()
{
    if(m_ctxCfg){
        delete m_ctxCfg;
        m_ctxCfg = nullptr;
    }

    if(m_dev){
        if(m_GlobaPipelinecache){
            m_dev->destroyPipelineCache(*m_GlobaPipelinecache);
            m_GlobaPipelinecache = nullptr;
        }

        m_dev->destroy(nullptr);
        m_dev = nullptr;
        SPDLOG_INFO("Destroyed vulkan device.");
    }

    m_mutexComputeQueues.clear();
}

bool kpextVkContext::destroyInstance()
{
#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS

    if(m_instance && m_debugMessenger){
        //        SPDLOG_INFO("{}", (void *)m_dynamicDispatcher.vkDestroyDebugUtilsMessengerEXT);
        m_instance->destroyDebugUtilsMessengerEXT(m_debugMessenger,
                                                  nullptr,
                                                  m_dynamicDispatcher);
        SPDLOG_INFO("Destroyed vulkan debugMessenger.");
    }

#endif

    bool ret = m_instance.get() == nullptr;

    // std::weak_ptr一般配合std::share_ptr使用， 用于获取引用计数之类
    //  use_count() 查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量。
    //  lock() 如果weak_ptr已经过期，则返回一个空的shared_ptr；
    //         反之，返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。
    //         返回非空shared_ptr会使引用计数加1
    std::weak_ptr<vk::Instance> instance(m_instance);

    if(instance.use_count() == 1){
        m_instance->destroy(nullptr);
        m_instance = nullptr;
        ret = true;
        SPDLOG_INFO("Destroyed vulkan instance.");
    }
    return ret;
}

bool kpextVkContext::getIsVkContextOk()
{
    bool ret = m_instance &&
            m_phyDev &&
            m_dev &&
            m_ComputeQueueFamilyIndices.size() > 0 &&
            m_ComputeQueues.size() == m_ComputeQueueFamilyIndices.size()
            ;

    return ret;
}

std::array<uint32_t, 3> kpextVkContext::getDefWorkGroupSize()
{
    std::array<uint32_t, 3> ret{16, 16, 1};
    size_t size = VK_DEF_COMPUTESHADER_WORKGROUP_SIZE.size();
    for (size_t i = 0; i < size; ++i) {
        auto v = VK_DEF_COMPUTESHADER_WORKGROUP_SIZE[i];
        if(v > 0) ret[i] = v;
    }
    return ret;
}

std::array<uint32_t, 3> kpextVkContext::getMaxWorkGroupCount()
{
    std::array<uint32_t, 3> ret{1, 1, 1};
    if(!m_phyDev){
        return ret;
    }

    vk::PhysicalDeviceProperties physicalDeviceProperties =
            m_phyDev->getProperties();

    auto maxWGC = physicalDeviceProperties.limits.maxComputeWorkGroupCount;
    ret[0] = maxWGC[0];
    ret[1] = maxWGC[1];
    ret[2] = maxWGC[2];
    return ret;
}

uint32_t kpextVkContext::getComputeQueuesCount()
{
    uint32_t ret;
    ret = static_cast<uint32_t>(m_ComputeQueues.size());
    return ret;
}

std::shared_ptr<kpext::CommandBuffer> kpextVkContext::cmdbuf(uint32_t queueIndex,
                                                             vk::CommandPoolCreateFlags poolflag,
                                                             uint32_t totalTimestamps)
{
    KPEXT_CTX_CHECK(kpext::CommandBuffer);

    if(queueIndex > m_ComputeQueues.size()){
        SPDLOG_WARN("Require queueIndex{} to create"
                    " kpext::CommandBuffer is invalid!");
        queueIndex = 0;
    }

    auto cmdbufptr =
            std::make_shared<kpext::CommandBuffer>(
                m_dev,
                m_ComputeQueues[queueIndex],
                m_ComputeQueueFamilyIndices[queueIndex],
                poolflag,
                true,
                totalTimestamps
                );

    cmdbufptr->setQueueAccessMutex(m_mutexComputeQueues[queueIndex]);

    return cmdbufptr;
}

std::shared_ptr<kpext::RawBuffer> kpextVkContext::buffer()
{
    KPEXT_CTX_CHECK(kpext::RawBuffer);
    auto buf = std::make_shared<kpext::RawBuffer>(
                m_phyDev,
                m_dev
                );
    return buf;
}

std::shared_ptr<kpext::RawDeviceMemory> kpextVkContext::deviceMem()
{
    KPEXT_CTX_CHECK(kpext::RawDeviceMemory);
    auto mem = std::make_shared<kpext::RawDeviceMemory>(
                m_dev,
                m_supportMemProps
                );
    return mem;
}

std::shared_ptr<kpext::BaseBuffer> kpextVkContext::baseBuffer(size_t size)
{
    KPEXT_CTX_CHECK(kpext::BaseBuffer);
    auto basebuf = std::make_shared<kpext::BaseBuffer>(
                buffer(),
                deviceMem(),
                size,
                vk::BufferUsageFlags(),
                vk::MemoryPropertyFlags()
                );
    return basebuf;
}

std::shared_ptr<kpext::StagingBuffer> kpextVkContext::stagingBuf(
        size_t size,
        bool coherent,
        bool cached
        )
{
    KPEXT_CTX_CHECK(kpext::StagingBuffer);
    auto stagingbuf = std::make_shared<kpext::StagingBuffer>(
                buffer(),
                deviceMem(),
                size,
                coherent,
                cached
                );
    return stagingbuf;
}

std::shared_ptr<kpext::DeviceBuffer> kpextVkContext::deviceBuf(size_t size)
{
    KPEXT_CTX_CHECK(kpext::DeviceBuffer);
    auto devicebuf = std::make_shared<kpext::DeviceBuffer>(
                buffer(),
                deviceMem(),
                size
                );
    return devicebuf;
}

std::shared_ptr<kpext::UniformBuffer> kpextVkContext::uniformBuf(size_t size)
{
    KPEXT_CTX_CHECK(kpext::UniformBuffer);

    auto uniformbuf = std::make_shared<kpext::UniformBuffer>(
                buffer(),
                deviceMem(),
                size
                );
    return uniformbuf;
}

bool kpextVkContext::checkUniformTexelBufElemSize(uint32_t size)
{
    if(!m_phyDev) return false;
    auto limits = m_phyDev->getProperties().limits;
    return limits.maxTexelBufferElements >= size;
}

std::shared_ptr<kpext::UniformTexelBuffer> kpextVkContext::uniformTexelBuf(size_t size,
                                                                           vk::Format format)
{
    auto uniformtexelbuf = std::make_shared<kpext::UniformTexelBuffer>(
                buffer(),
                deviceMem(),
                size,
                format
                );
    return uniformtexelbuf;
}

std::shared_ptr<kpext::DescriptorPool>
kpextVkContext::descriptorPool(const std::vector<uint32_t> &  poolCap,
                               uint32_t maxSetsCount,
                               vk::DescriptorPoolCreateFlags flags
                               )
{
    KPEXT_CTX_CHECK(kpext::DescriptorPool);

    auto descpool = std::make_shared<kpext::DescriptorPool>(
                m_dev,
                poolCap,
                maxSetsCount,
                flags
                );

    return descpool;
}

std::shared_ptr<kpext::ComputePipeline> kpextVkContext::computePipeline()
{
    KPEXT_CTX_CHECK(kpext::ComputePipeline);
    return std::make_shared<kpext::ComputePipeline>(m_dev,
                                                    m_GlobaPipelinecache);
}

#ifdef KPEXT_USE_KOMPUTE
std::shared_ptr<kp::Sequence> kpextVkContext::sequence(uint32_t queueIndex,
                                                       uint32_t totalTimestamps)
{
    KPEXT_CTX_CHECK(kp::Sequence);

    if(queueIndex > m_ComputeQueues.size()){
        SPDLOG_WARN("Require queueIndex{} to create"
                    " kp::Sequence is invalid!");
        queueIndex = 0;
    }

    std::shared_ptr<kp::Sequence> sq{ new kp::Sequence(
                    m_phyDev,
                    m_dev,
                    m_ComputeQueues[queueIndex],
                    m_ComputeQueueFamilyIndices[queueIndex],
                    totalTimestamps)
                                    };
    return sq;
}

std::shared_ptr<kp::TensorT<float>>
kpextVkContext::tensor(const std::vector<float> &data,
                       kp::Tensor::TensorTypes tensorType)
{

    KPEXT_CTX_CHECK(kp::TensorT<float>);

    SPDLOG_DEBUG("kp::Tensor<float> creation triggered");

    std::shared_ptr<kp::TensorT<float>> tensor{ new kp::TensorT<float>(
                    this->m_phyDev, this->m_dev, data, tensorType) };

    return tensor;
}

std::shared_ptr<kp::Tensor>
kpextVkContext::tensor(void *data,
                       uint32_t elementTotalCount,
                       uint32_t elementMemorySize,
                       const kp::Tensor::TensorDataTypes &dataType,
                       kp::Tensor::TensorTypes tensorType)
{
    KPEXT_CTX_CHECK(kp::Tensor);

    SPDLOG_DEBUG("kp::Tensor creation triggered");

    std::shared_ptr<kp::Tensor> tensor{
        new kp::Tensor(this->m_phyDev,
                       this->m_dev,
                       data,
                       elementTotalCount,
                       elementMemorySize,
                       dataType,
                       tensorType) };

    return tensor;
}

std::shared_ptr<kp::Tensor> kpextVkContext::imgF32C1(int cols, int rows, void *data)
{
    if(cols < 1 || rows < 1){
        if(cols < 1) cols = 1;
        if(rows < 1) rows = 1;
        SPDLOG_ERROR("use " __FUNCTION__ " with wrong input size!");
    }

    void * tempptr = data;
    std::shared_ptr<float> shareptr(nullptr);
    if(!data){
        shareptr = std::shared_ptr<float>(
                    new float[static_cast<size_t>(cols * rows)]
                );
        std::memset(shareptr.get(), 0,
                    static_cast<size_t>(cols * rows) * sizeof (float));
        tempptr = shareptr.get();
    }

    auto ret = std::make_shared<kp::Tensor>(
                this->m_phyDev,
                this->m_dev,
                tempptr,
                cols * rows,
                sizeof (float),
                kp::Tensor::TensorDataTypes::eFloat,
                kp::Tensor::TensorTypes::eDevice
                );

    return ret;
}

std::shared_ptr<kp::Tensor> kpextVkContext::imgU16C1(int cols, int rows,
                                                     void *data,
                                                     bool keepeven)
{
    if(cols < 1 || rows < 1 || rows * cols < 2){
        if(cols < 1) cols = 1;
        if(rows < 1) rows = 1;
        if(rows * cols < 2) cols = 2;
        SPDLOG_ERROR("use " __FUNCTION__ " with wrong input size!");
    }

    auto elemsize = static_cast<uint32_t>(cols * rows);
    if(keepeven) elemsize = elemsize + 1;
    elemsize = elemsize / 2;

    void * tempptr = data;
    std::shared_ptr<uint32_t> shareptr(nullptr);
    if(!data){
        shareptr = std::shared_ptr<uint32_t>(
                    new uint32_t[static_cast<size_t>(elemsize)]
                );
        std::memset(shareptr.get(), 0,
                    static_cast<size_t>(elemsize) * sizeof (uint32_t));
        tempptr = shareptr.get();
    }

    auto ret = std::make_shared<kp::Tensor>(
                this->m_phyDev,
                this->m_dev,
                tempptr,
                elemsize,
                sizeof (uint32_t),
                kp::Tensor::TensorDataTypes::eUnsignedInt,
                kp::Tensor::TensorTypes::eDevice
                );

    return ret;
}


std::shared_ptr<kp::Algorithm>
kpextVkContext::algorithm(const std::vector<std::shared_ptr<kp::Tensor>> &tensors,
                          const std::vector<uint32_t> &spirv,
                          const kp::Workgroup &workgroup,
                          const std::vector<float> &specializationConstants,
                          const std::vector<float> &pushConstants)
{
    KPEXT_CTX_CHECK(kp::Algorithm);

    SPDLOG_DEBUG("kp::Algorithm creation triggered");
    std::shared_ptr<kp::Algorithm> algorithm{ new kp::Algorithm(
                    this->m_dev,
                    tensors,
                    spirv,
                    workgroup,
                    specializationConstants,
                    pushConstants) };
    return algorithm;
}

std::shared_ptr<kp::Algorithm>
kpextVkContext::algorithm(const std::vector<std::shared_ptr<kp::Tensor>> &tensors,
                          const std::vector<uint32_t> &spirv,
                          const kp::Workgroup &workgroup,
                          const std::vector<int32_t> &specializationConstants,
                          const std::vector<int32_t> &pushConstants)
{
    KPEXT_CTX_CHECK(kp::Algorithm);

    SPDLOG_INFO("kp::Algorithm int32 creation triggered");
    SPDLOG_DEBUG("kp::Algorithm creation triggered");
    std::shared_ptr<kp::Algorithm> algorithm{ new kp::Algorithm(
                    this->m_dev,
                    tensors,
                    spirv,
                    workgroup,
                    specializationConstants,
                    pushConstants) };

    return algorithm;
}

std::shared_ptr<kp::Algorithm>
kpextVkContext::algorithm(const std::vector<std::shared_ptr<kp::Tensor>> &tensors,
                          const std::vector<uint32_t> &spirv,
                          const kp::Workgroup &workgroup,
                          const std::vector<uint32_t> &specializationConstants,
                          const std::vector<uint32_t> &pushConstants)
{
    KPEXT_CTX_CHECK(kp::Algorithm);

    SPDLOG_INFO("kp::Algorithm int32 creation triggered");
    SPDLOG_DEBUG("kp::Algorithm creation triggered");
    std::shared_ptr<kp::Algorithm> algorithm{ new kp::Algorithm(
                    this->m_dev,
                    tensors,
                    spirv,
                    workgroup,
                    specializationConstants,
                    pushConstants) };

    return algorithm;
}

#endif

void kpextVkContext::readJsonCfg()
{
    try {
        std::ifstream inputjson(m_cfgFilePath, std::ios::in);
        std::stringstream buffer;
        buffer << inputjson.rdbuf();

        auto jtemp = JSON::parse(buffer.str(), nullptr, false);
        if(jtemp.is_object()){
            *m_ctxCfg = jtemp;
            SPDLOG_INFO("{} Json config file found.", m_cfgFilePath);
            SPDLOG_DEBUG("{}", m_ctxCfg->dump(4,' ', false, JSON::error_handler_t::ignore));
        }else{
            SPDLOG_WARN("{} Json config file is not found or invalid.",
                        m_cfgFilePath);
        }

        JSON & cfg = *m_ctxCfg;
        if(!cfg["DevicesInfo"].is_array()){
            cfg["DevicesInfo"] = JSON({});
        }

        if(!cfg["Config"].is_object()){
            cfg["Config"] = JSON({
                                     {"ReqVKValidationLayer", nullptr},
                                     {"ReqPHYDevPUUID", nullptr},
                                     {"ReqQueueFamily", JSON::array({0})},
                                     {"DefComputeShaderWorkgroupSize", JSON::array({})},
                                     {"StagesThreadsCount", JSON::array({})},
                                 });
        }else{
            auto & array = cfg["Config"]["StagesThreadsCount"];
            if(array.is_array()){
                for(auto & item : array){
                    auto cnt = item.get<unsigned int>();
                    if(cnt < 1) cnt = 0;
                    if(cnt > 16) cnt = 16;
                    StagesThreadsCount.push_back(cnt);
                }
            }
        }
    }  catch (JSON::exception e) {
        SPDLOG_ERROR(e.what());
    }

}

std::string kpextVkContext::getPipelineUUIDFromJsonCfg()
{
    std::string ret{};
    try {
        auto & cfg = *m_ctxCfg;
        if(cfg["Config"].is_object()
                && cfg["Config"]["ReqPHYDevPUUID"].is_string()
                ){
            ret = cfg["Config"]["ReqPHYDevPUUID"].get<std::string>();
        }
    } catch (JSON::exception e) {
        SPDLOG_ERROR(e.what());
    }
    return ret;
}

std::vector<unsigned int> kpextVkContext::getQueueFamilyIndicesFromJsonCfg()
{
    std::vector<unsigned int> ret{};
    try {
        auto & cfg = *m_ctxCfg;
        if(cfg["Config"].is_object()
                && cfg["Config"]["ReqQueueFamily"].is_array()
                ){
            auto & ja = cfg["Config"]["ReqQueueFamily"];
            for(auto & jitem : ja){
                if(jitem.is_number()){
                    ret.push_back(jitem.get<unsigned int>());
                }
            }
        }
    } catch (JSON::exception e) {
        SPDLOG_ERROR(e.what());
    }
    return ret;
}

void kpextVkContext::updateJsonPhyDevInfo(nlohmann::json &jo,
                                          std::vector<vk::PhysicalDevice> & phydevs)
{
    try {
        jo["DevicesInfo"].clear();
        jo["DevicesInfo"] = JSON{};
        JSON devarray = JSON::array();
        for (auto & pd : phydevs) {
            auto prop = pd.getProperties();
            std::string pipecacheuid = fmt::format("{:02X}", fmt::join(prop.pipelineCacheUUID, ""));
            auto qfProps = pd.getQueueFamilyProperties();
            int qfindex = 0;

            JSON devinfo{};
            {  //  设备基本信息
                devinfo["Name"] = std::string(prop.deviceName.data());
                devinfo["PipelineCacheUUID"] = pipecacheuid;
            }


            { //  各队列家族属性
                auto qfinfo = JSON::array();
                for(auto & qfp : qfProps){
                    JSON propsinfo{};
                    std::vector<std::string> vflagss;
                    std::string flagss;
                    if(qfp.queueFlags & vk::QueueFlagBits::eGraphics){
                        vflagss.push_back("QUEUE_GRAPHICS");
                    }
                    if(qfp.queueFlags & vk::QueueFlagBits::eCompute){
                        vflagss.push_back("QUEUE_COMPUTE");
                    }
                    if(qfp.queueFlags & vk::QueueFlagBits::eTransfer){
                        vflagss.push_back("QUEUE_TRANSFER");
                    }

                    flagss = fmt::format("{}", fmt::join(vflagss, ", "));

                    propsinfo["Index"] = qfindex;
                    propsinfo["MaxCount"] = qfp.queueCount;
                    propsinfo["Flags"] = vflagss;
                    qfinfo.push_back(propsinfo);
                    qfindex++;
                }
                devinfo["QueueFamily"] = qfinfo;
            }

            {  // 物理设备资源限制信息
                const auto & limits = prop.limits;
                auto limitsinfo = JSON::array();
                auto jo = JSON::object();
                jo["maxComputeWorkGroupInvocations"] =
                        limits.maxComputeWorkGroupInvocations;
                limitsinfo.push_back(jo);

                auto uivec = std::vector<unsigned int>(
                            limits.maxComputeWorkGroupSize.begin(),
                            limits.maxComputeWorkGroupSize.end());
                jo.clear();
                jo["maxComputeWorkGroupSize(Shader)"] = JSON(uivec);
                limitsinfo.push_back(jo);

                uivec = std::vector<unsigned int>(
                            limits.maxComputeWorkGroupCount.begin(),
                            limits.maxComputeWorkGroupCount.end());
                jo.clear();
                jo["maxComputeWorkGroupCount(Dispatch)"] = JSON(uivec);
                limitsinfo.push_back(jo);

                jo.clear();
                jo["maxPushConstantsSize"] =
                        limits.maxPushConstantsSize;
                limitsinfo.push_back(jo);

                jo.clear();
                jo["maxUniformBufferRange"] =
                        limits.maxUniformBufferRange;
                limitsinfo.push_back(jo);

                jo.clear();
                jo["maxTexelBufferElements"] =
                        limits.maxTexelBufferElements;
                limitsinfo.push_back(jo);

                jo.clear();
                jo["maxPerStageDescriptorUniformBuffers"] =
                        limits.maxPerStageDescriptorUniformBuffers;
                limitsinfo.push_back(jo);

                jo.clear();
                jo["maxPerStageDescriptorStorageBuffers"] =
                        limits.maxPerStageDescriptorStorageBuffers;
                limitsinfo.push_back(jo);

                jo.clear();
                jo["maxPerStageDescriptorStorageImages"] =
                        limits.maxPerStageDescriptorStorageImages;
                limitsinfo.push_back(jo);

                devinfo["limits"] = limitsinfo;
            }

            {  // 感兴趣的纹素格式与纹理缓冲属性支持的关系
                auto texelbufformatinfo = JSON::array();
                auto jo = JSON::object();

                for (auto format : kpext::TexelBufInterestFormats) {
                    const auto fname = kpext::vkFormat2Str(format);
                    bool texelBufOk = false,
                            uniformTexelBufOk = false,
                            texelBufAtomicOk = false;

                    auto formatprops = pd.getFormatProperties(format);
                    kpext::checkTexelBufFormat(formatprops,
                                               &texelBufOk,
                                               &uniformTexelBufOk,
                                               &texelBufAtomicOk
                                               );
                    jo.clear();
                    jo[fname] = {
                        {"StorageTexelBuffer", texelBufOk},
                        {"UniformTexelBuffer", uniformTexelBufOk},
                        {"StorageTexelBufferAtomic", texelBufAtomicOk}};

                    texelbufformatinfo.push_back(jo);
                }
                devinfo["texelbuffer_format"] = texelbufformatinfo;
            }

            {  //  支持的扩展功能名
                auto extensionsname = JSON::array();
                {
                    auto extensionsprop = pd.enumerateDeviceExtensionProperties();
                    std::set<std::string> uniqueExtensionNames;
                    for (const vk::ExtensionProperties& ext : extensionsprop) {
                        uniqueExtensionNames.insert(ext.extensionName);
                    }

                    for(const auto & name : uniqueExtensionNames){
                        extensionsname.push_back(name);
                    }
                }

                devinfo["availableExtensions"] = extensionsname;
            }

            devarray.push_back(devinfo);

        } // for (auto & pd : phydevs)

        jo["DevicesInfo"] = devarray;
//        SPDLOG_INFO(jo.dump(4,' ', false, JSON::error_handler_t::ignore));
    }  catch (JSON::exception e) {
        SPDLOG_ERROR(e.what());
    }
}

void kpextVkContext::saveJsonToFile(nlohmann::json &jo, std::string filename)
{
    try {
        {
           std::ofstream outputjson(filename, std::ios::trunc | std::ios::out);
           outputjson << std::setw(4) << jo << std::endl;  // std::setw(4)设置输出宽度为4,默认为右对齐，空格填充
        }
    }  catch (std::exception e) {
        SPDLOG_ERROR(e.what());
    }
}


bool kpextVkContext::createInstance()
{
    if(m_instance){
        return true;
    }

    const std::vector<uint32_t> VK_API_VER_LIST(
                {
                    // VK_API_VERSION_1_2,
                    VK_API_VERSION_1_1,
                    VK_API_VERSION_1_0
                }
                );

    vk::ApplicationInfo applicationInfo;
#ifdef KPEXT_USE_KOMPUTE
    applicationInfo.pApplicationName = "Kompute";
    applicationInfo.pEngineName = "Kompute";
    applicationInfo.apiVersion = KOMPUTE_VK_API_VERSION;
    applicationInfo.engineVersion = KOMPUTE_VK_API_VERSION;
    applicationInfo.applicationVersion = KOMPUTE_VK_API_VERSION;
#else
    applicationInfo.pApplicationName = "KpExtLib";
    applicationInfo.pEngineName = "KpExtLib";
    applicationInfo.apiVersion = VK_API_VER_LIST[0]; // VK_API_VERSION_1_1;
    applicationInfo.engineVersion = VK_MAKE_API_VERSION(0,0,1,0);
#endif

    vk::InstanceCreateInfo instanceCreateInfo;

    if(!EnableDebugLayersByProgram && m_ctxCfg){
        try {
            auto & cfg = *m_ctxCfg;
            if(cfg["Config"]["ReqVKValidationLayer"].is_boolean()){
                auto enable = cfg["Config"]["ReqVKValidationLayer"].get<bool>();
                EnableDebugLayers = enable;
                const auto fs = "Set vulkan validation layer {} by config file '{}' !";
                if(IS_CONSOLE_SUPORT_COLOR){
                    SPDLOG_WARN(fs,
                                fmt::format(fmt::fg(fmt::color::yellow),
                                                "enable={}", enable),
                                m_cfgFilePath
                                );
                }else{
                    SPDLOG_WARN(fs,
                                fmt::format("enable={}",
                                                enable),
                                m_cfgFilePath
                                );
                }
            }

        }  catch (JSON::exception e) {
            SPDLOG_ERROR(e.what());
        }
    }

    if(VK_DEF_COMPUTESHADER_WORKGROUP_SIZE.size() < 1 && m_ctxCfg){
        try {
            auto & cfg = *m_ctxCfg;
            if(cfg["Config"]["DefComputeShaderWorkgroupSize"].is_array()){
                auto & ja = cfg["Config"]["DefComputeShaderWorkgroupSize"];
                for(auto & jitem : ja){
                    auto size = jitem.get<unsigned int>();
                    VK_DEF_COMPUTESHADER_WORKGROUP_SIZE.push_back(size);
                }
                if(ja.size() > 0){
                    auto workgroupsize = getDefWorkGroupSize();
                    const auto fstr = "compute shader default"
                                      " {} by json config file '{}' !";
                    std::string tstr;
                    if(IS_CONSOLE_SUPORT_COLOR){
                        tstr = fmt::format(fmt::fg(fmt::color::yellow),
                                           "workgroupsize set to [{}]",
                                           fmt::join(workgroupsize, ", ")
                                           );
                    } else {
                        tstr = fmt::format("workgroupsize set to [{}]",
                                           fmt::join(workgroupsize, ", ")
                                           );
                    }
                    tstr = fmt::format(fstr, tstr, m_cfgFilePath);
                    SPDLOG_WARN(tstr);
                }
            }
        }  catch (JSON::exception e) {
            SPDLOG_ERROR(e.what());
        }
    }

#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS

#ifdef SPDLOG_ACTIVE_LEVEL
#   if SPDLOG_ACTIVE_LEVEL > 5
    EnableDebugLayers = false;
#   endif
#endif

    std::vector<const char*> validLayerNames;
    std::vector<const char*> desiredLayerNames = {
        "VK_LAYER_LUNARG_assistant_layer",
        "VK_LAYER_LUNARG_standard_validation",
        "VK_LAYER_KHRONOS_validation",
    };
    std::vector<const char*> applicationExtensions;
    VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};

    if(EnableDebugLayers){
        SPDLOG_DEBUG("Adding vulkan debug validation layers");

        //  查找可用的层， 并打开能够打开的扩展层
        std::vector<std::string> envLayerNames;
#ifdef KPEXT_USE_KOMPUTE
        const char* envLayerNamesVal = std::getenv("KOMPUTE_ENV_DEBUG_LAYERS");
#else
        const char* envLayerNamesVal = std::getenv("KPEXT_ENV_DEBUG_LAYERS");
#endif
        if (envLayerNamesVal != nullptr && *envLayerNamesVal != '\0') {
            SPDLOG_DEBUG("Adding environment vulkan layers: {}",
                         envLayerNamesVal);
            std::istringstream iss(envLayerNamesVal);
            std::istream_iterator<std::string> beg(iss), end;
            envLayerNames = std::vector<std::string>(beg, end);
            for (const std::string& layerName : envLayerNames) {
                desiredLayerNames.push_back(layerName.c_str());
            }
            SPDLOG_DEBUG("Desired vulkan layers: {}", desiredLayerNames);
        }


        std::set<std::string> uniqueLayerNames;
        std::vector<vk::LayerProperties> availableLayerProperties =
                vk::enumerateInstanceLayerProperties();
        for (vk::LayerProperties layerProperties : availableLayerProperties) {
            std::string layerName(layerProperties.layerName.data());
            uniqueLayerNames.insert(layerName);
        }
        SPDLOG_DEBUG("Available vulkan layers: {}", uniqueLayerNames);
        for (const char* desiredLayerName : desiredLayerNames) {
            if (uniqueLayerNames.count(desiredLayerName) != 0) {
                validLayerNames.push_back(desiredLayerName);
            }
        }

        if (validLayerNames.size() > 0) {
            SPDLOG_DEBUG(
                        "Create vulkan instance with valid layers: {}",
                        validLayerNames);
            instanceCreateInfo.enabledLayerCount =
                    static_cast<uint32_t>(validLayerNames.size());
            instanceCreateInfo.ppEnabledLayerNames = validLayerNames.data();

            // instance 创建和释放时的调试信息回调对象设置
            debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
            debugCreateInfo.messageSeverity =
                    VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                    VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                    VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
            debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                    VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                    VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
            debugCreateInfo.pfnUserCallback = debugMsgCallback;
            debugCreateInfo.pUserData = nullptr;
            instanceCreateInfo.pNext = &debugCreateInfo;

        }else{
            SPDLOG_WARN("No valid layer names found from desired "
                        "layer names");
        }

        //  打开验证层调试信息反馈扩展功能
        applicationExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
        if (!applicationExtensions.empty()) {
            instanceCreateInfo.enabledExtensionCount =
                    static_cast<uint32_t>(applicationExtensions.size());
            instanceCreateInfo.ppEnabledExtensionNames =
                    applicationExtensions.data();
        }
    }
#endif

    instanceCreateInfo.pApplicationInfo = &applicationInfo;
    vk::Instance * instance = nullptr;

    for (const auto & v : VK_API_VER_LIST) {
        applicationInfo.apiVersion = v;
        instance = new vk::Instance;
        auto vkret = vk::createInstance(
                    &instanceCreateInfo, nullptr, instance);

        if(vkret != vk::Result::eSuccess){
            SPDLOG_ERROR("Can not create vulkan V{}.{} instance !"
                         " -- Error info : {}",
                         VK_VERSION_MAJOR(applicationInfo.apiVersion),
                         VK_VERSION_MINOR(applicationInfo.apiVersion),
                         vk::to_string(vkret)
                         );
            delete instance;
            instance = nullptr;
        }else{
            SPDLOG_INFO("Created vulkan V{}.{} instance !",
                         VK_VERSION_MAJOR(applicationInfo.apiVersion),
                         VK_VERSION_MINOR(applicationInfo.apiVersion)
                         );
            break;
        }
    }

    // 当前版本 kp::Manager::listDevices函数内是直接调用enumeratePhysicalDevices()的
    auto physicaldevices = instance->enumeratePhysicalDevices();

    //  更新json配置文件的一些信息
    if(m_ctxCfg){
        // 外部json配置文件生成
        updateJsonPhyDevInfo(*m_ctxCfg, physicaldevices);
        saveJsonToFile(*m_ctxCfg, m_cfgFilePath);
    }

    if(physicaldevices.size() < 1){
        SPDLOG_ERROR("can not find vulkan physical device!");
        instance->destroy(nullptr);
        delete instance;
        return true;
    }

    //  获取期望创建的设备号、设备的队列家族
    bool needupdateJsonCfg = false;
    bool nouseJsonCfg = kpextVkContext::NO_USE_JSON_CONFIG;
    std::vector<uint32_t> queueFamilyIndices{};
    if(VK_PHY_DEV_IDX_TO_CREATE < 0){
        if(VK_PHY_DEV_PUUID_TO_CREATE.empty() && !nouseJsonCfg){
            VK_PHY_DEV_PUUID_TO_CREATE = getPipelineUUIDFromJsonCfg();
        }
        if(VK_QUEUE_FAMILY_INDICES.size() < 1 && !nouseJsonCfg){
            queueFamilyIndices = getQueueFamilyIndicesFromJsonCfg();
        }else{
            queueFamilyIndices = VK_QUEUE_FAMILY_INDICES;
            nouseJsonCfg = true;
        }

        int phydevi = 0;
        for (auto & pd : physicaldevices) {
            auto prop = pd.getProperties();
            std::string pipecacheuid = fmt::format("{:02X}", fmt::join(prop.pipelineCacheUUID, ""));
            //            SPDLOG_INFO(pipecacheuid);
            //            SPDLOG_INFO(VK_PHY_DEV_PUUID_TO_CREATE);
            if(!pipecacheuid.empty()
                    && VK_PHY_DEV_PUUID_TO_CREATE == pipecacheuid
                    ){
                    VK_PHY_DEV_IDX_TO_CREATE = phydevi;
                    SPDLOG_INFO("Config to use vulkan device '{}({})'!",
                                prop.deviceName,
                                pipecacheuid
                                );
                    break;
            }
            phydevi ++;
        }

        if(VK_PHY_DEV_IDX_TO_CREATE < 0){
            VK_PHY_DEV_IDX_TO_CREATE = 0;
            VK_QUEUE_FAMILY_INDICES.clear();
            VK_QUEUE_FAMILY_INDICES.push_back(0);
            if(!VK_PHY_DEV_PUUID_TO_CREATE.empty()){
                SPDLOG_WARN("Can not find physicalDevice"
                            " with pipelinecacheuuid='{}'",
                            VK_PHY_DEV_PUUID_TO_CREATE
                            );
            }
            needupdateJsonCfg = true;
        }
    }else if(VK_PHY_DEV_IDX_TO_CREATE >= static_cast<int>(physicaldevices.size())){
        SPDLOG_ERROR("Can not use invalid vulkan physicalDevices[{}],"
                     " auto change to use physicalDevices[0]",
                     VK_PHY_DEV_IDX_TO_CREATE
                     );
        VK_PHY_DEV_IDX_TO_CREATE = 0;
        VK_QUEUE_FAMILY_INDICES.clear();
        VK_QUEUE_FAMILY_INDICES.push_back(0);
    }else{
        queueFamilyIndices = VK_QUEUE_FAMILY_INDICES;
    }

    //  遍历vulkan物理设备
    SPDLOG_INFO("");
    SPDLOG_INFO("---------Platform available vulkan devices Info-----------");
    int phydevi = 0;
    for (auto & pd : physicaldevices) {
        auto prop = pd.getProperties();
        std::string pipecacheuid = fmt::format("{:02X}", fmt::join(prop.pipelineCacheUUID, ""));
        SPDLOG_INFO("vulkan device '{}({})' found!",
                    prop.deviceName,
                    pipecacheuid
                    );

        SPDLOG_INFO("Limits Info:");
        printPhyDevLimits(prop.limits);

        SPDLOG_INFO("Queuefamlies Info:");
        auto qfProps = pd.getQueueFamilyProperties();

        if(VK_PHY_DEV_IDX_TO_CREATE == phydevi){
            std::map<unsigned int, unsigned int> idxcnt;
            std::vector<uint32_t> qfit{};
            VK_PHY_DEV_PUUID_TO_CREATE = pipecacheuid;
            for(auto idx : queueFamilyIndices){
                if(idx < qfProps.size()){
                    if(0 == idxcnt.count(idx)){
                        idxcnt[idx] = 0;
                    }
                    if(!(qfProps[idx].queueFlags & vk::QueueFlagBits::eCompute)){
                        SPDLOG_WARN("queueFamilys[{}] is not support compute shader ",
                                    idx);
                        continue;
                    }
                    if(idxcnt[idx] < qfProps[idx].queueCount){
                        qfit.push_back(idx);
                        idxcnt[idx]++;
                    }else{
                        SPDLOG_WARN("queueFamilys[{}] quantity exceeds limit", idx);
                    }
                }else{
                    SPDLOG_WARN("Invalid queueFamilys[{}]", idx);
                }
            }
            if(queueFamilyIndices != qfit){
                needupdateJsonCfg = true;
            }
            queueFamilyIndices.clear();
            queueFamilyIndices = qfit;
            VK_QUEUE_FAMILY_INDICES = queueFamilyIndices;
        }

        int qfindex = 0;
        for(auto & qfp : qfProps){
            //  打印信息
            std::vector<std::string> vflagss;
            std::string flagss;
            if(qfp.queueFlags & vk::QueueFlagBits::eGraphics){
                vflagss.push_back("QUEUE_GRAPHICS");
            }
            if(qfp.queueFlags & vk::QueueFlagBits::eCompute){
                vflagss.push_back("QUEUE_COMPUTE");
            }
            if(qfp.queueFlags & vk::QueueFlagBits::eTransfer){
                vflagss.push_back("QUEUE_TRANSFER");
            }

            flagss = fmt::format("{}", fmt::join(vflagss, ", "));
            SPDLOG_INFO("device {}"
                        " queueFamilys[{}] : flag = [{}],"
                        " queuesCount = {} ",
                        prop.deviceName,
                        qfindex,
                        flagss,
                        qfp.queueCount
                        );

            qfindex++;
        }

        //  感兴趣纹理缓冲格式支持信息打印
        bool printtexlbufinfo = true;
        for (auto format : kpext::TexelBufInterestFormats) {
            const auto fname = kpext::vkFormat2Str(format);
            bool texelBufOk = false,
                    uniformTexelBufOk = false,
                    texelBufAtomicOk = false;

            auto formatprops = pd.getFormatProperties(format);
            kpext::checkTexelBufFormat(formatprops,
                                       &texelBufOk,
                                       &uniformTexelBufOk,
                                       &texelBufAtomicOk
                                       );
            if(printtexlbufinfo){
                if(!texelBufOk || !uniformTexelBufOk || !texelBufAtomicOk){
                    SPDLOG_INFO("TexelBuffer Info:");
                    printtexlbufinfo = false;
                }
            }

            if(!texelBufOk){
                SPDLOG_INFO("StorageTexelBuffer not support"
                            " Texel format {}",
                            fname
                            );
            }
            if(!uniformTexelBufOk){
                SPDLOG_INFO("UniformTexelBuffer not support"
                            " Texel format {}",
                            fname
                            );
            }
            if(!texelBufAtomicOk){
                SPDLOG_INFO("StorageTexelBufferAtomic not support"
                            " Texel format {}",
                            fname
                            );
            }
        }

        phydevi++;

        SPDLOG_INFO("-------------------------------------------------------");
        SPDLOG_INFO("");
    }


    if(VK_PHY_DEV_IDX_TO_CREATE < 0){
        VK_PHY_DEV_IDX_TO_CREATE = 0;
        VK_QUEUE_FAMILY_INDICES.clear();
        VK_QUEUE_FAMILY_INDICES.push_back(0);
        needupdateJsonCfg = true;
    }
    if(VK_QUEUE_FAMILY_INDICES.size() < 1){
        VK_QUEUE_FAMILY_INDICES.push_back(0);
        needupdateJsonCfg = true;
    }
    //   保存选择的物理设备到json配置文件
    if(needupdateJsonCfg
            && !nouseJsonCfg
            && static_cast<int>(physicaldevices.size()) > VK_PHY_DEV_IDX_TO_CREATE
            ){
        try {
            auto & cfg = *m_ctxCfg;
            auto prop = physicaldevices[static_cast<size_t>(VK_PHY_DEV_IDX_TO_CREATE)]
                    .getProperties();
            std::string pipecacheuid = fmt::format("{:02X}",
                                                   fmt::join(prop.pipelineCacheUUID, ""));
            cfg["Config"]["ReqPHYDevPUUID"].clear();
            cfg["Config"]["ReqPHYDevPUUID"] = pipecacheuid;
            cfg["Config"]["ReqQueueFamily"] = JSON(VK_QUEUE_FAMILY_INDICES);
            saveJsonToFile(*m_ctxCfg, m_cfgFilePath);
        }  catch (JSON::exception e) {
            SPDLOG_ERROR(e.what());
        }
    }

#ifndef KOMPUTE_DISABLE_VK_DEBUG_LAYERS
    if(EnableDebugLayers && validLayerNames.size() > 0){
        //  创建校验层的调试信息回调对象
        SPDLOG_DEBUG("Adding debug callbacks to vulkan instance");
        vk::DebugUtilsMessengerCreateInfoEXT dbgcallbackci;
        {
            using Severity = vk::DebugUtilsMessageSeverityFlagBitsEXT;
            using MsgType = vk::DebugUtilsMessageTypeFlagBitsEXT;

#ifdef SPDLOG_ACTIVE_LEVEL
#            if SPDLOG_ACTIVE_LEVEL > 1
            dbgcallbackci.messageSeverity =
                    Severity::eInfo |
                    Severity::eWarning |
                    Severity::eError
                    ;
#            elif SPDLOG_ACTIVE_LEVEL > 2
            dbgcallbackci.messageSeverity =
                    Severity::eWarning |
                    Severity::eError
                    ;
#            elif SPDLOG_ACTIVE_LEVEL > 3
            dbgcallbackci.messageSeverity =
                    Severity::eWarning
                    ;
#            else
            dbgcallbackci.messageSeverity =
                    Severity::eVerbose |
                    Severity::eInfo |
                    Severity::eWarning |
                    Severity::eError
                    ;
#            endif
#else
            dbgcallbackci.messageSeverity =
                    Severity::eVerbose |
                    Severity::eInfo |
                    Severity::eWarning |
                    Severity::eError
                    ;
#endif

            dbgcallbackci.messageType =
                    MsgType :: eGeneral |
                    MsgType :: ePerformance |
                    MsgType :: eValidation
                    ;
        }
        dbgcallbackci.pfnUserCallback = debugMsgCallback;


        m_dynamicDispatcher.init(*instance, &vkGetInstanceProcAddr);
//        SPDLOG_INFO("{}", (void *)m_dynamicDispatcher.vkCreateDebugUtilsMessengerEXT);
//        SPDLOG_INFO("{}", (m_debugMessenger ? true : false));
        instance->createDebugUtilsMessengerEXT(
                    &dbgcallbackci, nullptr,
                    &m_debugMessenger, m_dynamicDispatcher
                    );
//        SPDLOG_INFO("{}", (m_debugMessenger ? true : false));
        if(m_debugMessenger){
            using Severity = vk::DebugUtilsMessageSeverityFlagBitsEXT;
            using MsgType = vk::DebugUtilsMessageTypeFlagBitsEXT;
            vk::DebugUtilsMessengerCallbackDataEXT data;
            data.pMessage = "Vulkan validation layer is enable!";
            instance->submitDebugUtilsMessageEXT(
                        Severity::eWarning,
                        MsgType::ePerformance,
                        data,  //  设置为nullptr可触发校验层报错
                        m_dynamicDispatcher
                        );
        }

    }
#endif

    m_instance = std::shared_ptr<vk::Instance>(instance);
    return true;
}

void kpextVkContext::createDevice(const std::vector<unsigned int> &familyQueueIndices,
                                  unsigned int physicalDeviceIndex,
                                  const std::vector<std::string> &desiredExtensions)
{
    std::vector<unsigned int> fqueueIndices{familyQueueIndices};
    if(fqueueIndices.size() < 1){
        fqueueIndices.push_back(0);
    }

    SPDLOG_INFO("");
    SPDLOG_INFO("-------- Going to create vulkan logical device ------------");
    {   // 打印选择中的设备号和队列家族
        SPDLOG_INFO(
                    fmt::format("Require to create vulkan devices[{}],"
                                " queueFamilyIndices=[{}] with extensions[{}]",
                                physicalDeviceIndex,
                                fmt::join(fqueueIndices, ", "),
                                fmt::join(desiredExtensions, ", ")
                                )
                    );
    }


    SPDLOG_DEBUG("Creating vulkan device.");
    if (this->m_instance == nullptr) {
        SPDLOG_ERROR("Vulkan instance is null!");
        return;
    }

    //  检查要创建的设备是否存在于平台中
    auto physicalDevices = m_instance->enumeratePhysicalDevices();
    uint32_t deviceCount = 0;
    STCAST_ASSIGN(deviceCount, physicalDevices.size());

    if(deviceCount == 0){
        SPDLOG_ERROR("No vulkan physical device support in current platform!");
        return;
    }

    if(physicalDeviceIndex >= deviceCount){
        SPDLOG_ERROR("Vulkan physical device index:{} not provided!",
                     physicalDeviceIndex
                     );
        return;
    }


    vk::PhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];

    {  //  检查要创建的队列家族索引是否满足需要
        vk::PhysicalDeviceProperties physicalDeviceProperties =
          physicalDevice.getProperties();

        SPDLOG_INFO("");
        SPDLOG_INFO("Selected physical devices[{}]:'{}' is found.",
                    physicalDeviceIndex,
                    physicalDeviceProperties.deviceName);

        std::vector<vk::QueueFamilyProperties> allQueueFamilyProperties =
                physicalDevice.getQueueFamilyProperties();

        size_t sizecount = fqueueIndices.size();
        for (unsigned int i = 0; i < sizecount; ++i) {
            auto fqidx = fqueueIndices[i];
            if(fqidx >= allQueueFamilyProperties.size()){
                throw std::runtime_error(
                            fmt::format("QueueFamilyIndex{} is not exists",
                                        fqidx
                                        )
                            );
            }
            vk::QueueFamilyProperties queueFamilyProperties =
              allQueueFamilyProperties[fqidx];
            if (!(queueFamilyProperties.queueFlags &
                  vk::QueueFlagBits::eCompute) ){
                throw std::runtime_error(
                            fmt::format("QueueFamilyIndex{} is not support compute shader",
                                        fqidx
                                        )
                            );
            }
        }
    }

    //   获取每个队列家族要创建的队列数量和队列优先级
    std::unordered_map<uint32_t, uint32_t> familyQueueCounts; // {{fqueueindex, count},...}
    std::unordered_map<uint32_t, std::vector<float>> familyQueuePriorities; //  {{fqueueindex, priority},...}
    for (const auto& idx : fqueueIndices) {
        familyQueueCounts[idx]++;
        familyQueuePriorities[idx].push_back(1.0f);
    }

    //  获取创建逻辑设备的信息
    std::unordered_map<uint32_t, uint32_t> getQueueByIndex;  //  用于查询获取queue对象， 下面的代码将其初始化为0
    std::vector<vk::DeviceQueueCreateInfo> deviceQueueCreateInfos;  //  创建逻辑设备队列的信息
    for (const auto& familyQueueInfo : familyQueueCounts) {
        // familyQueueInfo  {fqueueindex, count}
        getQueueByIndex[familyQueueInfo.first] = 0;

        vk::DeviceQueueCreateInfo deviceQueueCreateInfo(
                    vk::DeviceQueueCreateFlags(),
                    familyQueueInfo.first,  // queueFamilyIndex
                    familyQueueInfo.second,  // queueCount
                    familyQueuePriorities[familyQueueInfo.first].data()  //pQueuePriorities
                );
        deviceQueueCreateInfos.push_back(deviceQueueCreateInfo);
    }

    //  检查设备支持的扩展功能
    std::vector<vk::ExtensionProperties> deviceExtensions =
            physicalDevice.enumerateDeviceExtensionProperties();

    std::set<std::string> uniqueExtensionNames;
    for (const vk::ExtensionProperties& ext : deviceExtensions) {
        uniqueExtensionNames.insert(ext.extensionName);
    }

    SPDLOG_DEBUG("Vulkan physicalDevices[{}] available extensions {}",
                 physicalDeviceIndex,
                 uniqueExtensionNames);

    std::vector<const char*> validExtensions;
    for (const std::string& ext : desiredExtensions) {
        if (uniqueExtensionNames.count(ext) != 0) {
            validExtensions.push_back(ext.c_str());
        }else{
            SPDLOG_ERROR("Vulkan physicalDevices[{}] "
                         "not support '{}' extension!",
                         physicalDeviceIndex,
                         ext
                         );
        }
    }

    if(validExtensions.size() > 0){
        auto str = fmt::format("ValidExtensions[{}] "
                               "is going to enable",
                               fmt::join(validExtensions, ", "));
        if(validExtensions.size() != kpextVkContext::VK_DESIRED_DEV_EXTENSIONS.size()){
            SPDLOG_WARN(str);
        }else{
            SPDLOG_INFO(str);
        }

    }

    vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(),
                                          static_cast<uint32_t>(deviceQueueCreateInfos.size()),
                                          deviceQueueCreateInfos.data(),
                                          {},
                                          {},
                                          static_cast<uint32_t>(validExtensions.size()),
                                          validExtensions.data());

    auto device = std::make_shared<vk::Device>();
    auto vkRet = physicalDevice.createDevice(
                &deviceCreateInfo, nullptr, device.get());
    if(vkRet == vk::Result::eSuccess){
        SPDLOG_INFO("Vulkan logical device created");
        SPDLOG_INFO("------------------------------------------------");
        SPDLOG_INFO("");
    }else{
        SPDLOG_INFO("Vulkan logical device failed!");
        return;
    }

    m_phyDev = std::make_shared<vk::PhysicalDevice>(physicalDevice);
    m_dev = device;
    m_ComputeQueueFamilyIndices.clear();
    m_ComputeQueueFamilyIndices = fqueueIndices;
    m_ComputeQueues.clear();

    for (const uint32_t& familyQueueIndex : m_ComputeQueueFamilyIndices) {
        std::shared_ptr<vk::Queue> currQueue = std::make_shared<vk::Queue>();
//        SPDLOG_DEBUG("get familyQueue[{}], at index={}",
//                     familyQueueIndex,
//                     getQueueByIndex[familyQueueIndex]
//                     );
        m_dev->getQueue(familyQueueIndex,
                        getQueueByIndex[familyQueueIndex],
                        currQueue.get());

        getQueueByIndex[familyQueueIndex]++;  //  队列家族中的下一个队列

        m_ComputeQueues.push_back(currQueue);
        m_mutexComputeQueues.push_back(
                    std::make_shared<std::mutex>()
                    );
    }

    m_supportMemProps = m_phyDev->getMemoryProperties();

    m_GlobaPipelinecache = std::make_shared<vk::PipelineCache>();
    vk::PipelineCacheCreateInfo pipelineCacheInfo =
            vk::PipelineCacheCreateInfo();
    m_dev->createPipelineCache(
                &pipelineCacheInfo,
                nullptr,
                m_GlobaPipelinecache.get()
                );
}


